package unamur.iodass.host;

import java.util.ArrayList;
import java.util.HashMap;
import org.apache.log4j.Logger;
import unamur.iodass.communication.Communication;
import unamur.iodass.communication.messages.BookDeal;
import unamur.iodass.communication.messages.BookStore;
import unamur.iodass.communication.messages.ContratBookStore;

/**
 * Classe traitant le rôle Host du BookStore, il n'y a qu'un seul Host par traitement d'une enchère.
 * 
 * @author Cherchem, Marchal
 * 
 */

public class HostHandler {
	private static final Logger logger = Logger.getLogger(HostHandler.class);

	private static volatile HostHandler instance;

	private HashMap<Long, ArrayList<BookStore>> auctions; // liste (isbn, bookstoresParticipant)

	private HashMap<Long, Integer> minimums; // liste (isbn, prixMinimum)

	private static Communication com;


	public static HostHandler getInstance() {
		if (instance == null) {
			synchronized (HostHandler.class) {
				if (instance == null) {
					instance = new HostHandler();
				}
			}
		}
		return instance;
	}


	private HostHandler() {
		auctions = new HashMap<Long, ArrayList<BookStore>>();
		minimums = new HashMap<Long, Integer>();
		com = Communication.getInstance();


	}


	public void newDeal(long isbn, int minimumInitial, ArrayList<BookStore> listBS) {	

		for (int i=0; i<listBS.size(); i++) {
			com.async(new BookDeal(null, isbn, minimumInitial, 1, com.BOOKSTORE_IP, com.BOOKSTORE_PORT),
					listBS.get(i).getIp(), listBS.get(i).getPort());

		}

		minimums.put(isbn, minimumInitial);

	}

	/**
	 * 
	 *   Cette méthode gère les objets bookDeal envoyés par les bookstore.
	 * Elle réenvoie de nouveaux objets bookDeal aux bookstores
	 * jusqu'à ce qu'aucun ne puisse proposer un prix inférieur.
	 *   A ce moment, la méthode envoie un objet ContratBookStore à OnlineLibrary
	 * et un ContratBookStore au bookstore ayant gagné le marché cette enchère particulière.
	 * 
	 * @param bookDeal 
	 * 				message représentant une offre pour un livre
	 */
	synchronized public void handleBookDeal(BookDeal bookDeal) {
		
		logger.info("HOST :: " + bookDeal.getBookStore().getName() +"\t "+ bookDeal.getIsbn() +"\t "+ bookDeal.getMinimum());

		// BookStore renvoie -1 : se retirer du deal, il ne peut faire une proposition inférieur
		// BookStore renvoie 0 : livre indisponible auprès de ce BookStore
		if ((bookDeal.getMinimum() == -1) || (bookDeal.getMinimum() == 0)) { 

			// verifier si le bookstore est toujours actif pour le deal
			if (bookDeal.getMinimum() == -1 && containsBookStore(bookDeal.getIsbn(), bookDeal.getBookStore())) { 		
				if ((sizeOfBookStores(bookDeal.getIsbn()) == 1) ){ // Il ne reste qu'un seul BookStore
					
					logger.info("CONCLURE : " + bookDeal.getBookStore().getName() + "\t " + bookDeal.getIsbn() + "\t " + minimums.get(bookDeal.getIsbn()));

					/*
					 * Envoie du contrat à OnlineLibrary et au BookStore ayant proposé le prix minimum
					 */
					
					ContratBookStore contrat = new ContratBookStore(bookDeal.getBookStore(), bookDeal.getIsbn(), minimums.get(bookDeal.getIsbn()));
					
					com.async(contrat, com.ONLINELIBRARY_IP, com.ONLINELIBRARY_PORT);
					com.async(contrat, contrat.getBookStore().getIp(), contrat.getBookStore().getPort());
					
					
					
					retirerBookStore(bookDeal.getIsbn(), bookDeal.getBookStore());
					
				}else if (sizeOfBookStores(bookDeal.getIsbn()) > 1) {
					retirerBookStore(bookDeal.getIsbn(), bookDeal.getBookStore());
					logger.info("\t\tBOOKSTORE LEFT : "+ bookDeal.getBookStore().getName() + " ISBN : " + bookDeal.getIsbn());
				}
			} 

			else if (bookDeal.getMinimum() == 0) { // BookStore n'a pas le livre disponible
				logger.info(bookDeal.getBookStore().getName() + "\t" + "n'a pas le livre isbn : " + bookDeal.getIsbn());
			}
		}

		// minBookStore < minGlobal : Le bookStore propose une offre inférieur à notre demande
		else {
			if (bookDeal.getMinimum() < minimums.get(bookDeal.getIsbn()))  {
				changeMinimum(bookDeal.getIsbn(), bookDeal.getMinimum());
				ajoutBookStore(bookDeal.getIsbn(), bookDeal.getBookStore());
				
				int round = bookDeal.getRound() + 1;

				ArrayList<BookStore> bookStores = auctions.get(bookDeal.getIsbn());
				for (int i=0; i<bookStores.size(); i++) {
					
					com.async(new BookDeal(null, bookDeal.getIsbn(), bookDeal.getMinimum(), round, com.BOOKSTORE_IP, com.BOOKSTORE_PORT),
							bookStores.get(i).getIp(), bookStores.get(i).getPort());

				}

				logger.info("******  NEW ROUND "+ bookDeal.getRound() + " avec le prix minimum : " + bookDeal.getMinimum());
			}
		}
	}

	private int sizeOfBookStores(long isbn) {
		if (auctions.containsKey(isbn)){
			return auctions.get(isbn).size();
		}
		return 0;
	}

	private void ajoutBookStore(long isbn, BookStore bs) {
		if(!auctions.containsKey(isbn)) {
			ArrayList<BookStore> arrayList = new ArrayList<BookStore>();
			arrayList.add(bs);
			auctions.put(isbn, arrayList);
		}

		if (auctions.containsKey(isbn)) {
			(auctions.get(isbn)).add(bs);
		}
	}


	private boolean containsBookStore(long isbn, BookStore bs) {
		if (!auctions.containsKey(isbn)) {
			return false;
		}else {
			for(int i=0; i<auctions.get(isbn).size(); i++) {
				if (auctions.get(isbn).get(i).equals(bs)) {
					return true;
				}
			}
			return false;
		}
	}


	private void retirerBookStore(long isbn, BookStore bs) {
		if (auctions.containsKey(isbn))
			for(int i=0; i<auctions.get(isbn).size(); i++) {
				if (auctions.get(isbn).get(i).equals(bs)) {
					auctions.get(isbn).remove(i);
				}
			}
	}


	private void changeMinimum(long isbn, int newMinimum) {
		minimums.put(isbn, newMinimum);
	}


	public  HashMap<Long, Integer> getMinimums() {
		return minimums;
	}



}
